/*
 * Copyright (c) 2020, yifengling0. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. No personal names or organizations' names associated with the
 *    Atomthreads project may be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "iosdrv.h"

#include <string.h>

#include "atom.h"
#include "pin.h"
#include "drv_usart.h"
#include "stm8l15x_usart.h"

static void usart_buffered_send(SioBase* sio)
{
    Stm8lUsart *usart = (Stm8lUsart*)sio;

    USART_ITConfig(usart->Protected.owner, USART_IT_TXE, ENABLE);
}


static void usart_blocking_send(SioBase* sio, const char *buffer, uint32_t size)
{
    Stm8lUsart *usart = (Stm8lUsart*)sio;

    for(int i=0; i < size; i++){
        USART_SendData8(usart->Protected.owner, buffer[i]);
        while (USART_GetFlagStatus(usart->Protected.owner, USART_FLAG_TXE) == RESET);
    }
}

static int usart_ioctl(SioBase*sio, int function, void* arg)
{
    //TODO 波特率修改，等其他扩展功能
    return NG;
}

static USART_StopBits_TypeDef get_stop_bit(enSioStopBit stopBit)
{
    if(stopBit == ssb2Bit){
        return USART_StopBits_2;
    }else{
        return USART_StopBits_1;
    }
}

static USART_Parity_TypeDef get_parity(enSioParity parity)
{
    USART_Parity_TypeDef ret = USART_Parity_No;
    
    switch(parity){
        case spNone:
        ret = USART_Parity_No;
        break;
        case spOdd:
        ret = USART_Parity_Even;
        break;
        case spEven:
        ret = USART_Parity_Odd;
        break;
        default:
        break;
    }

    return (ret);
}

void uart_rx_handler(Stm8lUsart *sio)
{
    if(USART_GetITStatus(sio->Protected.owner, USART_IT_RXNE) == SET){
        USART_ClearITPendingBit(sio->Protected.owner, USART_IT_RXNE);
        uint8_t c = USART_ReceiveData8(sio->Protected.owner);
        ringPut(&sio->Protected.ops.rxRing, (char*)&c, 1);
    }
}

void uart_tx_handler(Stm8lUsart *sio)
{
   if(USART_GetITStatus(sio->Protected.owner, USART_IT_TXE) == SET){
        USART_ClearITPendingBit(sio->Protected.owner, USART_IT_TXE);
        int count = 0;
        char c;
        count = ringGet(&sio->Protected.ops.txRing, &c, 1);
        if( count > 0){
            USART_SendData8(sio->Protected.owner, c);
        }else{
            USART_ITConfig(sio->Protected.owner, USART_IT_TXE, DISABLE);
        }
    }
}

typedef struct {
    USART_TypeDef* owner;
    Stm8lUsart *usart;
}Stm8lUsartHandler;

static Stm8lUsartHandler _usart_handler[] = {
#ifdef BSP_USE_USART1    
    {USART1, NULL},
#endif
#ifdef BSP_USE_USART2    
    {USART2, NULL},
#endif
#ifdef BSP_USE_USART3        
    {USART3, NULL},
#endif
#ifndef COM_USE_DBGPORT  //  for compile
    {NULL, NULL}
#endif
};

#ifdef BSP_USE_USART3
void USART3_RX_HANDLER()
{
    /* Call the interrupt entry routine */
    atomIntEnter();

    uart_rx_handler(_usart_handler[USART3_INDEX].usart);

    /* Call the interrupt exit routine */
    atomIntExit(FALSE);
}

void USART3_TX_HANDLER()
{
    /* Call the interrupt entry routine */
    atomIntEnter();

    uart_tx_handler(_usart_handler[USART3_INDEX].usart);

    /* Call the interrupt exit routine */
    atomIntExit(FALSE);
}
#endif

void Stm8lUartCreate(Stm8lUsart* sio, const char*name, Stm8lUsartConfig *config, uint32_t rxSize, uint32_t txSize) 
{
    _usart_handler[config->index].usart = sio;

    sio->Protected.owner = _usart_handler[config->index].owner;

    sio->Protected.ops.Ioctl = usart_ioctl;
    sio->Protected.ops.BufferedSend = usart_buffered_send;
    sio->Protected.ops.BlockingSend = usart_blocking_send;

    SioCreate(&sio->super, name, &config->param, &sio->Protected.ops, config->rxBuf, rxSize, config->txBuf, txSize);

    sio->Protected.config = config;
    
    CLK_PeripheralClockConfig(config->clk, ENABLE);
    
    PinMode(config->rxpin, PIN_MODE_INPUT);
    PinMode(config->txpin, PIN_MODE_OUTPUT);
    
    USART_Init (sio->Protected.owner, config->param.baud, 
                config->param.dataBits == 8 ? USART_WordLength_8b : USART_WordLength_9b, 
                get_stop_bit(config->param.stopBit), 
                get_parity(config->param.parity),
                (USART_Mode_TypeDef)(USART_Mode_Tx | USART_Mode_Rx));

    USART_ITConfig(sio->Protected.owner, USART_IT_RXNE, ENABLE);
    USART_ITConfig(sio->Protected.owner, USART_IT_TXE, DISABLE);

    USART_Cmd(sio->Protected.owner, ENABLE);
}
